#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
用户数据模型

定义用户相关的数据模型，包括：
1. SQLAlchemy ORM 模型 - 用于数据库操作
2. Pydantic 模型 - 用于数据验证和序列化

功能特性：
- 用户注册和登录
- 密码加密存储
- 用户信息管理
- 数据验证和序列化

作者: FastAPI 学习项目
创建时间: 2024
"""

from datetime import datetime
from typing import Optional, List
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text
from sqlalchemy.orm import relationship
from pydantic import BaseModel, EmailStr, validator, Field

from app.database.connection import Base


class User(Base):
    """
    用户 SQLAlchemy ORM 模型
    
    定义用户表的数据库结构和关系
    
    Attributes:
        id: 用户唯一标识符
        username: 用户名（唯一）
        email: 邮箱地址（唯一）
        hashed_password: 加密后的密码
        full_name: 用户全名
        is_active: 用户是否激活
        is_superuser: 是否为超级用户
        avatar_url: 头像URL
        bio: 个人简介
        created_at: 创建时间
        updated_at: 更新时间
        items: 用户拥有的物品列表（关联关系）
    """
    
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, index=True, comment="用户ID")
    username = Column(
        String(50), 
        unique=True, 
        index=True, 
        nullable=False,
        comment="用户名"
    )
    email = Column(
        String(100), 
        unique=True, 
        index=True, 
        nullable=False,
        comment="邮箱地址"
    )
    hashed_password = Column(
        String(255), 
        nullable=False,
        comment="加密密码"
    )
    full_name = Column(
        String(100), 
        nullable=True,
        comment="用户全名"
    )
    is_active = Column(
        Boolean, 
        default=True,
        comment="是否激活"
    )
    is_superuser = Column(
        Boolean, 
        default=False,
        comment="是否为超级用户"
    )
    avatar_url = Column(
        String(255), 
        nullable=True,
        comment="头像URL"
    )
    bio = Column(
        Text, 
        nullable=True,
        comment="个人简介"
    )
    created_at = Column(
        DateTime, 
        default=datetime.utcnow,
        comment="创建时间"
    )
    updated_at = Column(
        DateTime, 
        default=datetime.utcnow, 
        onupdate=datetime.utcnow,
        comment="更新时间"
    )
    
    # 关联关系：用户拥有的物品
    items = relationship("Item", back_populates="owner", cascade="all, delete-orphan")
    
    def __repr__(self):
        """
        用户对象的字符串表示
        
        Returns:
            str: 用户对象的描述字符串
        """
        return f"<User(id={self.id}, username='{self.username}', email='{self.email}')>"
    
    def to_dict(self):
        """
        将用户对象转换为字典
        
        Returns:
            dict: 包含用户信息的字典
        """
        return {
            "id": self.id,
            "username": self.username,
            "email": self.email,
            "full_name": self.full_name,
            "is_active": self.is_active,
            "is_superuser": self.is_superuser,
            "avatar_url": self.avatar_url,
            "bio": self.bio,
            "created_at": self.created_at,
            "updated_at": self.updated_at
        }


# Pydantic 模型用于数据验证和序列化

class UserBase(BaseModel):
    """
    用户基础 Pydantic 模型
    
    包含用户的基本信息字段
    """
    username: str = Field(
        ..., 
        min_length=3, 
        max_length=50,
        description="用户名，长度3-50个字符"
    )
    email: EmailStr = Field(
        ...,
        description="邮箱地址"
    )
    full_name: Optional[str] = Field(
        None, 
        max_length=100,
        description="用户全名，最多100个字符"
    )
    bio: Optional[str] = Field(
        None, 
        max_length=500,
        description="个人简介，最多500个字符"
    )
    avatar_url: Optional[str] = Field(
        None,
        description="头像URL"
    )
    
    @validator('username')
    def validate_username(cls, v):
        """
        验证用户名格式
        
        Args:
            v: 用户名值
            
        Returns:
            str: 验证后的用户名
            
        Raises:
            ValueError: 当用户名格式不正确时抛出异常
        """
        if not v.isalnum() and '_' not in v and '-' not in v:
            raise ValueError('用户名只能包含字母、数字、下划线和连字符')
        return v.lower()
    
    @validator('bio')
    def validate_bio(cls, v):
        """
        验证个人简介内容
        
        Args:
            v: 个人简介值
            
        Returns:
            str: 验证后的个人简介
        """
        if v and len(v.strip()) == 0:
            return None
        return v


class UserCreate(UserBase):
    """
    创建用户的 Pydantic 模型
    
    继承用户基础模型，添加密码字段
    """
    password: str = Field(
        ..., 
        min_length=8, 
        max_length=100,
        description="密码，长度8-100个字符"
    )
    confirm_password: str = Field(
        ...,
        description="确认密码"
    )
    
    @validator('password')
    def validate_password(cls, v):
        """
        验证密码强度
        
        Args:
            v: 密码值
            
        Returns:
            str: 验证后的密码
            
        Raises:
            ValueError: 当密码不符合要求时抛出异常
        """
        if len(v) < 8:
            raise ValueError('密码长度至少8个字符')
        if not any(c.isupper() for c in v):
            raise ValueError('密码必须包含至少一个大写字母')
        if not any(c.islower() for c in v):
            raise ValueError('密码必须包含至少一个小写字母')
        if not any(c.isdigit() for c in v):
            raise ValueError('密码必须包含至少一个数字')
        return v
    
    @validator('confirm_password')
    def passwords_match(cls, v, values):
        """
        验证确认密码是否匹配
        
        Args:
            v: 确认密码值
            values: 其他字段值
            
        Returns:
            str: 验证后的确认密码
            
        Raises:
            ValueError: 当两次密码不匹配时抛出异常
        """
        if 'password' in values and v != values['password']:
            raise ValueError('两次输入的密码不匹配')
        return v


class UserUpdate(BaseModel):
    """
    更新用户信息的 Pydantic 模型
    
    所有字段都是可选的，只更新提供的字段
    """
    username: Optional[str] = Field(
        None, 
        min_length=3, 
        max_length=50,
        description="用户名"
    )
    email: Optional[EmailStr] = Field(
        None,
        description="邮箱地址"
    )
    full_name: Optional[str] = Field(
        None, 
        max_length=100,
        description="用户全名"
    )
    bio: Optional[str] = Field(
        None, 
        max_length=500,
        description="个人简介"
    )
    avatar_url: Optional[str] = Field(
        None,
        description="头像URL"
    )
    is_active: Optional[bool] = Field(
        None,
        description="是否激活"
    )
    
    @validator('username')
    def validate_username(cls, v):
        """
        验证用户名格式
        """
        if v and (not v.isalnum() and '_' not in v and '-' not in v):
            raise ValueError('用户名只能包含字母、数字、下划线和连字符')
        return v.lower() if v else v


class UserResponse(UserBase):
    """
    用户响应的 Pydantic 模型
    
    用于 API 响应，包含用户的公开信息
    """
    id: int = Field(
        ...,
        description="用户ID"
    )
    is_active: bool = Field(
        ...,
        description="是否激活"
    )
    is_superuser: bool = Field(
        ...,
        description="是否为超级用户"
    )
    created_at: datetime = Field(
        ...,
        description="创建时间"
    )
    updated_at: datetime = Field(
        ...,
        description="更新时间"
    )
    
    class Config:
        """
        Pydantic 配置
        
        启用 ORM 模式以支持从 SQLAlchemy 模型创建
        """
        from_attributes = True
        json_encoders = {
            datetime: lambda v: v.isoformat()
        }


class UserLogin(BaseModel):
    """
    用户登录的 Pydantic 模型
    
    用于用户登录验证
    """
    username: str = Field(
        ...,
        description="用户名或邮箱"
    )
    password: str = Field(
        ...,
        description="密码"
    )


class UserPasswordChange(BaseModel):
    """
    修改密码的 Pydantic 模型
    
    用于用户修改密码
    """
    current_password: str = Field(
        ...,
        description="当前密码"
    )
    new_password: str = Field(
        ..., 
        min_length=8,
        description="新密码"
    )
    confirm_new_password: str = Field(
        ...,
        description="确认新密码"
    )
    
    @validator('new_password')
    def validate_new_password(cls, v):
        """
        验证新密码强度
        """
        if len(v) < 8:
            raise ValueError('密码长度至少8个字符')
        if not any(c.isupper() for c in v):
            raise ValueError('密码必须包含至少一个大写字母')
        if not any(c.islower() for c in v):
            raise ValueError('密码必须包含至少一个小写字母')
        if not any(c.isdigit() for c in v):
            raise ValueError('密码必须包含至少一个数字')
        return v
    
    @validator('confirm_new_password')
    def passwords_match(cls, v, values):
        """
        验证新密码确认
        """
        if 'new_password' in values and v != values['new_password']:
            raise ValueError('两次输入的新密码不匹配')
        return v